# -*- coding: utf8 -*-

__author__ = 'sergey'

import traceback
from time import sleep, time
import gobject
from datetime import datetime, timedelta
from threading import Thread, Event, Lock
from multiprocessing import cpu_count

class FindDupImages(Thread):
    """
    __init__ : args = ( app, ui, db_req )
    """

    app = None
    ui = None
    store = None
    lock_ui = None
    lock_stat = None

    db_req = None

    stop_event = None

    log = None

    images_count = 0
    processed = 0
    similar = 0
    t_begin = 0
    stats = None

    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, verbose=None):
        self.app, self.ui, self.db_req = args
        self.stop_event = Event()
        self.lock_ui = Lock()
        self.lock_stat = Lock()
        self.stats = []
        Thread.__init__(self, group, target, name, args, kwargs, verbose)

    def run(self):
        from logger import get_file_logger
        self.log = get_file_logger("worker-FindDupImages")
        try:
            self.find_dupimages()
        except Exception as e:
            self.log.error(e)
            self.log.error(traceback.format_exc())
        return 0


    def match_func(self, model, iter, data):
        column, key = data # data is a tuple containing column number, key
        value = model.get_value(iter, column)
        return value == key

    def search(self, model, iter, func, data):
        while iter:
            if func(model, iter, data):
                return iter
            result = self.search(model, model.iter_children(iter), func, data)
            if result: return result
            iter = model.iter_next(iter)
        return None


    def add_image_to_treeview(self, image1, image2, image2image):

        return self

        self.lock_ui.acquire()

        img1_id_str = str(image2image["image1_id"])
        img2_id_str = str(image2image["image2_id"])

        img1_iter = self.search( self.store, self.store.iter_children(None), self.match_func, (0, img1_id_str,) )

        if not img1_iter:
            img1_iter = self.store.append( None, [ img1_id_str, None, None, None, image1["name"], ] )

        self.store.append( img1_iter, [
            None,
            img2_id_str,
            image2image["equal_proc"],
            image2image["quality_points"],
            image2["name"],
        ] )

        self.lock_ui.release()

        return self


    def format_seconds(self, seconds):

        s = int(seconds)

        m = (s - s % 60) / 60
        s %= 60
        h = (m - m % 60) / 60
        m %= 60
        d = (h - h % 24) / 24
        h %= 24

        return "%d дня, %02d:%02d:%02d" % (d, h, m, s,)


    def update_ui(self):

        t_now = time()
        if t_now - self.t_last < 0.1:
            return

        self.t_last = t_now

        n_stats = 100


        self.lock_ui.acquire()

        itemNow = {
            "time":t_now,
            "proc":self.processed,
            "similar":self.similar,
            "count":self.images_count
        }

        self.stats.append(itemNow)

        count = len(self.stats)

        if count > n_stats:
            self.stats = self.stats[1:]
            count -= 1

        stats = self.stats[:]

        self.lock_ui.release()


        sum_seconds = 0

        for item in stats:
            if item["proc"] < itemNow["proc"]:
                sum_seconds += (itemNow["count"] - itemNow["proc"]) / (itemNow["proc"] - item["proc"]) * (t_now - item["time"])
            else:
                sum_seconds += (itemNow["count"] - itemNow["proc"]) * (t_now - item["time"])

        if count > 0:
            eta = self.format_seconds( sum_seconds / count )
        else:
            eta = self.format_seconds( sum_seconds )


        p = itemNow["proc"] * 1.0 / itemNow["count"]
        if p < 0:
            p = 0
        if p > 1:
            p = 1

        s = itemNow["similar"] * 1.0 / itemNow["count"]
        if s < 0:
            s = 0
        if s > 1:
            s = 1

        gobject.idle_add( self.ui.get_object("progressbar_nonuniq_images").set_fraction, p )
        gobject.idle_add( self.ui.get_object("progressbar_nonuniq_images").set_text,
            "Обработано: %.3f из %d (%0.3f%%); Похожих: %d (%0.3f%%)" % (
                itemNow["proc"], itemNow["count"], p,
                itemNow["similar"], s
            )
        )
        gobject.idle_add( self.ui.get_object("label_images_eta").set_text, eta )

        return self

    def inc_processed(self, p):
        #self.lock_stat.acquire()
        self.processed += p
        #self.lock_stat.release()
        return self

    def inc_similar(self):
        #self.lock_stat.acquire()
        self.similar += 1
        #self.lock_stat.release()
        return self


    def find_dupimages(self):

        self.log.info("START")

        from worker.search_dupimages_vectors import FindDupImagesVectors

        treeview2 = self.ui.get_object("treeview2")
        self.store = treeview2.get_model()

        self.store.clear()

        self.log.debug("Clean images dup table...")
        self.db_req.query("clean_image2image", ())
        self.db_req.query("clean_file2file", ())


        self.log.debug("Get images count...")
        t1 = datetime.now()
        self.images_count = self.db_req.query("get_images_count", ())
        t2 = datetime.now()
        self.log.debug("Got images count: %s, in %s" % (self.images_count, t2 - t1))

        self.processed = 0
        self.similar = 0
        self.t_last = self.t_begin = time()
        self.stats = []

        if not self.images_count:
            self.log.info("STOP")
            return 0

        self.update_ui()

        offset = 0
        count = 100

        self.log.debug("Get offset=%d, count=%d" % (offset, count,))

        req_id = self.db_req.query("get_images", (count, offset), no_return=True )
        t1 = datetime.now()
        while not self.db_req.has_result(req_id):
            if self.stop_event.isSet():
                break
            sleep(0.01)
        t2 = datetime.now()
        self.log.debug("Got images in %s" % (t2 - t1,))

        threads = []
        threads_count = cpu_count()

        self.log.info("Start %s threads" % threads_count)

        for i in range(threads_count):
            th = FindDupImagesVectors(args=(self, self.db_req, i,))
            th.start()
            threads.append(th)

        while not self.stop_event.isSet():
            item = self.db_req.get_result_row(req_id)

            # self.log.debug("Item=%r" % item)
            # print("-- item: %r" % (item,))
            if item is None:
                if offset + count >= self.images_count:
                    break
                offset += count
                self.log.debug("Get offset=%d, count=%d" % (offset, count,))
                req_id = self.db_req.query("get_images", (count, offset), no_return=True )
                t1 = datetime.now()
                while not self.db_req.has_result(req_id):
                    if self.stop_event.isSet():
                        break
                    sleep(0.01)
                t2 = datetime.now()
                self.log.debug("Got images in %s" % (t2 - t1,))

                continue

            added = False
            while not added:
                if self.stop_event.isSet():
                    break
                for i in range(threads_count):
                    if threads[ i ].empty():
                        threads[ i ].add_image(item)
                        added = True
                        break
                sleep(0.01)

        self.log.info("Wait while threads processed all images...")

        if self.stop_event.isSet():
            for i in range(threads_count):
                threads[i].stop()
        else:
            empty = False
            while not empty:
                empty = True
                for i in range(threads_count):
                    if not threads[i].empty():
                        empty = False
                if self.stop_event.isSet():
                    empty = True
                sleep(0.01)

        self.log.info("Stop threads")

        for i in range(threads_count):
            threads[i].stop()

        self.log.info("Wait threads to stop...")

        alive = True
        while alive:
            alive = False
            for i in range(threads_count):
                if threads[i].isAlive():
                    alive = True
            sleep(0.01)

        self.log.info("STOP")

        self.stop_event.clear()

        return
